﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using JBeamLibrary.Tools;
using JBeamLibrary.VehicleParts;
using JBeamLibrary.VehicleParts.Elements;
using JColor = JBeamLibrary.VehicleParts.Elements.Color;
using Color = System.Drawing.Color;

namespace DriveBeamEditor.Controls.BeamNG.Controls
{
    public partial class ConfiguratorControl : VehicleControl
    {
        #region Fields

        private bool _isMouseDown;
        private bool _isChecking;

        #endregion

        #region Properties

        private TreeNode RootNode
        {
            get { return configTree.Nodes["Root"]; }
        }

        private TreeNode MainNode
        {
            get { return RootNode.FirstNode; }
        }

        #endregion

        #region CTORs

        public ConfiguratorControl()
        {
            InitializeComponent();
        }

        #endregion

        #region Events

        #region Binding sources

        private void bindingSourceConfigs_AddingNew(object sender, AddingNewEventArgs e)
        {
            if (DesignMode || Vehicle == null)
            {
                bindingSourceConfigs.CancelEdit();
                gridConfigs.NotifyCurrentCellDirty(true);
                gridConfigs.CancelEdit();
                gridConfigs.NotifyCurrentCellDirty(false);
                return;
            }

            /* Avoid loop */
            if (e.NewObject is VehicleConfig) return;

            /* Create new config */
            e.NewObject = new VehicleConfig(Vehicle, string.Format("{0}.pc", Randomizer.GetRandomName()));

            /* Add the current elements to the new config */
            UpdateConfig(e.NewObject as VehicleConfig);
        }

        private void bindingSourceConfigs_CurrentChanged(object sender, EventArgs e)
        {
            if (bindingSourceConfigs.Current == null) BindDefaults();
            else BindConfig(bindingSourceConfigs.Current as VehicleConfig);
            configTree.Nodes["Root"].EnsureVisible();

            /* Update box text */
            boxButtons.Text =
                bindingSourceConfigs.Current != null
                    ? string.Format("Configuration: {0}", ((VehicleConfig)bindingSourceConfigs.Current).DisplayName)
                    : "Default configuration";
        }

        #endregion

        #region Configs grid

        private void gridConfigs_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            gridConfigs_CellContentClick(sender, e);
        }

        private void gridConfigs_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            /* Clicks on headers */
            if (e.ColumnIndex < 0 || e.RowIndex < 0) return;

            /* Get column & row & cell */
            var col = gridConfigs.Columns[e.ColumnIndex];
            var row = gridConfigs.Rows[e.RowIndex];

            /* Validate new row */
            if (row.IsNewRow && col == colDeleteConfig)
            {
                bindingSourceConfigs.EndEdit();
                gridConfigs.NotifyCurrentCellDirty(true);
                gridConfigs.EndEdit();
                gridConfigs.NotifyCurrentCellDirty(false);
            }

            /* Delete the current row */
            else if (col == colDeleteConfig)
            {
                gridConfigs.Rows.RemoveAt(e.RowIndex);
            }
        }

        #endregion

        #region Tree view

        private void configTree_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Copy;
        }

        private void configTree_DragDrop(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(typeof(VehiclePart))) return;

            var part = (VehiclePart)e.Data.GetData(typeof(VehiclePart));
            var pt = configTree.PointToClient(new Point(e.X, e.Y));
            var parentNode = configTree.GetNodeAt(pt);
            var newNode = new TreeNode(part.Name);
            parentNode.Nodes.Add(newNode);
            parentNode.ExpandAll();
        }

        private void configTree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;

            e.Node.Checked = !e.Node.Checked;
            UpdateConfig();
        }

        private void configTree_BeforeCheck(object sender, TreeViewCancelEventArgs e)
        {
            /* Disallow manual checkbox actions */
            if (e.Action != TreeViewAction.Unknown)
            {
                e.Cancel = true;
                return;
            }

            /* Hierarchical uncheck down */
            if (e.Node.Checked)
            {
                foreach (var child in e.Node.Nodes.OfType<TreeNode>())
                    child.Checked = false;
            }

            if (_isChecking) return;
            if (e.Node.Parent == null) return;

            /* Disallow checking nodes for which parent is not checked */
            if (!e.Node.Parent.Checked)
            {
                e.Cancel = true;
                return;
            }

            /* Do not affect slot's brothers */
            if (e.Node.Tag is Slot) return;

            /* Uncheck brothers */
            _isChecking = true;
            foreach (var brother in e.Node.Parent.Nodes.OfType<TreeNode>().Where(brother => brother != e.Node))
                brother.Checked = false;
            _isChecking = false;
        }

        private void configTree_MouseDown(object sender, MouseEventArgs e)
        {
        }

        private void configTree_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {
            /* Variables */
            var l = e.Node.Bounds.X + e.Node.Level * 10;
            var t = e.Node.Bounds.Y;
            var b = e.Node.Bounds.Bottom;
            var r = e.Node.Bounds.Right + e.Node.Level * 10;
            var w = e.Node.Bounds.Width;
            var h = e.Node.Bounds.Height;
            var bnds = new Rectangle(l, t, w * 2, h);

            /* Dots for checked parts */
            if (e.Node.Tag is VehiclePart && e.Node.Checked)
            {
                var checkBounds = new RectangleF(l - 10f, t + t / 2f - 4f, 8f, 8f);
                e.Graphics.FillEllipse(Brushes.OrangeRed, checkBounds);
            }

            /* Squares for slots */
            if (e.Node.Tag is Slot)
            {
                var slotBounds = new RectangleF(l - 10f, t + t / 2f - 4f, 8f, 8f);
                e.Graphics.FillRectangle(Brushes.RoyalBlue, slotBounds);
            }

            /* Parts text */
            var nodeFont = e.Node.NodeFont ?? ((TreeView)sender).Font;
            if (e.Node.Tag is VehiclePart)
            {
                e.Graphics.DrawString(e.Node.Text, nodeFont, Brushes.Navy, bnds);
            }
            else
            {
                e.Graphics.DrawString(e.Node.Text, nodeFont, Brushes.Silver, bnds);
            }

            /* Focus */
            if ((e.State & TreeNodeStates.Focused) == 0) return;
            using (var focusPen = new Pen(Color.Black))
            {
                focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                var focusBounds = bnds;
                focusBounds.Size = new Size(focusBounds.Width - 1, focusBounds.Height - 1);
                e.Graphics.DrawRectangle(focusPen, focusBounds);
            }
        }

        #endregion

        #endregion

        #region Methods

        public override string ToString()
        {
            return "Configurator";
        }

        private void BindConfigs()
        {
            gridConfigs.Enabled = false;
            bindingSourceConfigs.DataSource = null;

            if (Vehicle != null)
                bindingSourceConfigs.DataSource = Vehicle.Configurations;

            gridConfigs.Refresh();
            gridConfigs.Enabled = true;
        }

        private void BindDefaults()
        {
            var root = configTree.Nodes["Root"];
            root.Nodes.Clear();
            AddParts(root, null);
            root.Checked = true;
            CheckDefaults(root);
            root.ExpandAll();
        }

        private void BindConfig(VehicleConfig config)
        {
            var root = configTree.Nodes["Root"];
            root.Nodes.Clear();
            AddParts(root, null);
            root.Checked = true;
            CheckConfig(root, config);
            root.ExpandAll();
        }

        private void AddSlotParts(TreeNode parentNode, Slot parentSlot)
        {
            var parts = Vehicle.GetPartsPerSlot(parentSlot == null ? "main" : parentSlot.TypeName);
            foreach (var part in parts)
            {
                var node = new TreeNode(string.Format("{0}: {1}", (parentSlot != null ? parentSlot.Description : "Main"), part.Description));
                node.Tag = new KeyValuePair<Slot, VehiclePart>(parentSlot, part);
                parentNode.Nodes.Add(node);

                foreach (var slot in part.Slots)
                    AddSlotParts(node, slot);
            }
        }

        private void AddParts(TreeNode parentNode, Slot parentSlot)
        {
            if (Vehicle == null) return;
            var parts = Vehicle.GetPartsPerSlot(parentSlot == null ? "main" : parentSlot.TypeName);
            foreach (var part in parts)
            {
                var node = new TreeNode("Part: " + part.Description);
                node.Tag = part;
                parentNode.Nodes.Add(node);

                AddSlots(node, part);
            }
        }

        private void AddSlots(TreeNode parentNode, VehiclePart parentPart)
        {
            foreach (var slot in parentPart.Slots)
            {
                var node = new TreeNode("Slot: " + (slot != null ? slot.Description : "Main"));
                node.Tag = slot;
                parentNode.Nodes.Add(node);

                AddParts(node, slot);
            }
        }

        private void CheckDefaults(TreeNode parentNode)
        {
            if (Vehicle == null) return;
            foreach (var child in parentNode.Nodes.OfType<TreeNode>())
            {
                if (child.Tag is Slot)
                    child.Checked = true;
                else
                {
                    var part = child.Tag as VehiclePart;
                    if (part == null) continue;

                    var slot = child.Parent.Tag as Slot;
                    if (slot == null || slot.DefaultPartName.Equals(part.Name))
                    {
                        child.Checked = true;
                    }
                }

                CheckDefaults(child);
            }
        }

        private void CheckConfig(TreeNode parentNode, VehicleConfig config)
        {
            if (Vehicle == null) return;
            foreach (var child in parentNode.Nodes.OfType<TreeNode>())
            {
                if (child.Tag is Slot)
                    child.Checked = true;
                else
                {
                    var part = child.Tag as VehiclePart;
                    if (part == null) continue;

                    var slot = child.Parent.Tag as Slot;
                    if (slot == null || (config.Contains(slot.TypeName) && config[slot.TypeName].Value != null && config[slot.TypeName].Value.Name.Equals(part.Name)))
                    {
                        child.Checked = true;
                    }
                }

                CheckConfig(child, config);
            }
        }

        private void UpdateConfig(VehicleConfig config = null, TreeNode parentNode = null)
        {
            if (config == null) config = bindingSourceConfigs.Current as VehicleConfig;
            if (config == null) return;
            if (parentNode == null) parentNode = MainNode;
            if (parentNode == null) return;

            foreach (var child in parentNode.Nodes.OfType<TreeNode>())
            {
                if (child.Tag is Slot)
                    UpdateConfig(config, child);
                else
                {
                    var part = child.Tag as VehiclePart;
                    if (part == null) continue;

                    var slot = child.Parent.Tag as Slot;
                    if (slot != null && child.Checked)
                    {
                        if (config.Contains(slot.TypeName))
                            config.Remove(slot.TypeName);
                        config.Add(new KeyValuePair<Slot, VehiclePart>(slot, part));
                    }
                    else if (slot != null && !child.Checked && config.Contains(slot.TypeName) && config[slot.TypeName].Value == part)
                    {
                        config.Remove(slot.TypeName);
                    }
                }

                UpdateConfig(config, child);
            }
        }

        protected override void OnVehicleChanged()
        {
            BindConfigs();
            BindDefaults();
            configTree.Nodes["Root"].EnsureVisible();
            base.OnVehicleChanged();
        }

        #endregion
    }
}
